package cn.huyanbing.middleware.thread.research;

import java.util.concurrent.TimeUnit;

/**
 * @BelongProject middleware
 * @BelongPackage cn.huyanbing.middleware.thread.research
 * @Description TODO
 * @ClassName SimpleLimiterPermitCount
 * @Author huyangbing
 * @Date 2024/11/22 00:03
 * @Version 1.0.0
 */
public class SimpleLimiterPermitCount {

    //当前令牌桶中的数量
    long storedPermits = 0;

    //令牌桶的容量
    long maxPermits = 6;

    //下一个令牌产生时间
    long next = System.nanoTime();

    //发放令牌间隔：纳秒
    long interval = 1000_000_000;


    //请求时间在下一令牌产生时间之后，则1
    // 1、重新计算令牌桶中的令牌数
    // 2、将下一个令牌发放时间重置为当前时间
    void resync(long now) {
        if (now > next) {
            //新产生的令牌数 这段时间产生了很多令牌
            long newPermits = (now - next) / interval;
            //新令牌增加到令牌桶
            storedPermits = Math.min(maxPermits, storedPermits + newPermits);

            //将下一个令牌发放时间重置为当前时间
            next = now;
        }
    }

    //预占令牌，返回能够获取令牌的时间
    synchronized long reserve(long now) {
        resync(now);

        //能够获取令牌的时间
        long at = next;

        //令牌桶总能提供的令牌 fb 只能为0 或者1
        long fb = Math.min(1, storedPermits);

        //令牌净需求：首先减掉令牌桶中的令牌
        long nr = 1 - fb;

        //重新计算下一令牌产生时间
        next = next + nr * interval;

        //重新计算令牌桶中的令牌
        this.storedPermits -= fb;

        return at;
    }


    //申请令牌
    void acquire() {
        //申请令牌时的时间
        long now = System.nanoTime();
        //预占令牌
        long at = reserve(now);

        long waitTime = Math.max(at - now, 0);

        //按照条件等待
        if (waitTime > 0) {
            try {
                TimeUnit.NANOSECONDS.sleep(waitTime);
            } catch (InterruptedException e) {

            }

        }

    }


    public static void main(String[] args) {
        SimpleLimiterPermitCount simpleLimiter = new SimpleLimiterPermitCount();

        long prev = System.nanoTime();
        for (int i = 0; i < 100; i++) {
            simpleLimiter.acquire();

            long next = System.nanoTime();

            System.out.println((next - prev) / 1000_000);


            if (i % 6 == 0) {
                //睡3秒 说明能 产生3个令牌
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {

                }
            }


            prev = next;
        }
    }
}
